Originally Posted by
Pole
Any suggestions?
Most C compilers, GCC in particular, do not consider the address of a specific constant array element as a constant. (This sounds like a silly limitation, but I believe this is because the elements themselves do not have individual symbols at compile time. I am too lazy to check the C standards whether this is expected behaviour or a bug in the compiler, but I believe this is expected behaviour.)
The workaround is to define each string as a separate variable (a static constant character array). Then, wherever you would use a string literal, you can just use the name of that variable instead. There is no run-time overhead, either. (In fact, this technique often saves run-time read-only memory, if you use the same string repeatedly. Many compilers store the same exact string multiple times, if defined as string literal multiple times. This technique avoids that, too.)
Here is an example:
Code:
#include <stdlib.h>
#include <stdio.h>
#define ENGLISH 0
#define POLISH 1
#define LANGUAGES 2
#define DEFINE_MESSAGE(name, english, polish) \
static const char msg_en_ ## name [] = english, \
msg_pl_ ## name [] = polish
#define MESSAGES(name) \
{ msg_en_ ## name, msg_pl_ ## name }
#define ENGLISH_MESSAGE(name) \
msg_en_ ## name
#define POLISH_MESSAGE(name) \
msg_pl_ ## name
/* The following three DEFINE_MESSAGES() lines are equivalent to
* static const char msg_en_intro[] = "Hello";
* static const char msg_pl_intro[] = "Witamy";
* static const char msg_en_menu1[] = "Option1";
* static const char msg_pl_menu1[] = "Opcja1";
* static const char msg_en_menu2[] = "Option2";
* static const char msg_pl_menu2[] = "Opcja2";
*/
DEFINE_MESSAGE(intro, "Hello", "Witamy");
DEFINE_MESSAGE(menu1, "Option1", "Opcja1");
DEFINE_MESSAGE(menu2, "Option2", "Opcja2");
/* This is how to define the original messages array using the above.
* Note, however, that you are unlikely to actually need this array.
*/
static const char *const message[][LANGUAGES] = {
#define MSG_INTRO 0
/* 0 */ MESSAGES(intro),
#define MSG_MENU1 1
/* 1 */ MESSAGES(menu1),
#define MSG_MENU2 2
/* 2 */ MESSAGES(menu2),
#define MSG_COUNT 3
};
/* This is a structure that describes one menu entry,
* with text in each language.
*/
struct menuitem {
const char *const message[LANGUAGES];
const int action;
};
static const struct menuitem menu = { MESSAGES(intro), 1 };
/* This structure contains only one language text.
*/
struct langmenu {
const char *const message;
const int action;
};
static const struct langmenu menu2 = { POLISH_MESSAGE(menu2), 2 };
int main(void)
{
int msg, lang;
for (msg = 0; msg < MSG_COUNT; msg++)
for (lang = 0; lang < LANGUAGES; lang++)
printf("message[%d][%d] = \"%s\"\n", msg, lang, message[msg][lang]);
printf("menu.message[ENGLISH] = \"%s\"\n", menu.message[ENGLISH]);
printf("menu.message[POLISH] = \"%s\"\n", menu.message[POLISH]);
printf("menu.action = %d\n", menu.action);
printf("menu2.message = \"%s\"\n", menu2.message);
printf("menu2.action = %d\n", menu.action);
printf("ENGLISH_MESSAGE(menu1) = \"%s\"\n", ENGLISH_MESSAGE(menu1));
return EXIT_SUCCESS;
}
In short, the above code uses the preprocessor macros to shorten the variable definitions. Each string is contained in a separate variable, but because the variable names use a simple logic, the MESSAGES() macro can construct an array of pointers to the desired string in each language. Further languages are quite easy and straightforward to add, if desired.
The ENGLISH_MESSAGE() and POLISH_MESSAGE() macros are a convenience, so that you do NOT need to try and remember how the original string variable names are constructed. You should always use these, so that if there is a namespace collision (that is, the variable names clash with ones you'd like to use), you can simply change how the string variables are named.
Questions?